package com.baibei.ebec.sdk;

import android.net.Uri;
import android.text.TextUtils;
import android.util.Base64;

import com.blankj.utilcode.utils.LogUtils;

import java.net.URLEncoder;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * 调试签名用
 * 加密工具
 * Created by ChenRui on 2017/5/23 0023 15:14.
 */
public final class SDKEncryptUtils {

    public static final String KEY_MAC_HMAC_MD5 = "HmacMD5";
    public static final String KEY_MAC_HMAC_SHA1 = "HmacSHA1";
    public static final String KEY_MAC_HMAC_SHA256 = "HmacSHA256";
    public static final String KEY_MAC_HMAC_SHA384 = "HmacSHA384";
    public static final String KEY_MAC_HMAC_SHA512 = "HmacSHA512";
    public static final int ENCODE_TYPE_HEX = 0;
    public static final int ENCODE_TYPE_BASE64 = 1;

    private static final String ALGORITHM = "RSA";

    private static final char[] hexArray = "0123456789ABCDEF".toCharArray();

    private static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    private static final String DEFAULT_CHARSET = "UTF-8";

    // 默认HMAC 算法
    private static final String DEFAULT_HMAC = KEY_MAC_HMAC_SHA256;

    /**
     * 密码加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    public static String encrypt(String password) {
        return Base64.encodeToString(password.getBytes(), Base64.DEFAULT);
    }


    /**
     * AES CBC模式加密
     *
     * @param content 加密内容
     * @param key     密码 16位
     * @param iv      偏移量 16位
     */
    public static String encrypt(String content, String key, String iv) {
        if (TextUtils.isEmpty(content) || TextUtils.isEmpty(key) || TextUtils.isEmpty(iv))
            return content;
        if (key.length() < 16 || iv.length() < 16) {
            return content;
        }
        try {

            byte[] raw = key.getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //"算法/模式/补码方式"
            IvParameterSpec ivParam = new IvParameterSpec(iv.getBytes()); //使用CBC模式，需要一个向量iv，可增加加密算法的强度
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParam);
            byte[] encrypted = cipher.doFinal(content.getBytes());

            return Base64.encodeToString(encrypted, Base64.NO_WRAP);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return content;
    }

    /**
     * AES CBC模式解密
     *
     * @param content 解密文本
     * @param key     16位密码
     * @param iv      16位偏移量
     * @throws Exception
     */
    public static String decrypt(String content, String key, String iv) throws Exception {
        byte[] raw = key.getBytes();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //"算法/模式/补码方式"
        IvParameterSpec ivParam = new IvParameterSpec(iv.getBytes()); //使用CBC模式，需要一个向量iv，可增加加密算法的强度
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParam);
        byte[] encrypted = Base64.decode(content, Base64.DEFAULT);//先用base64解密
        byte[] original = cipher.doFinal(encrypted);
        return new String(original);
    }


    /**
     * byte to hex
     *
     * @param bytes
     * @return
     */
    public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    /**
     * HMAC加密
     *
     * @param KEY_MAC    算法类型，参考常量KEY_MAC_*
     * @param key        私钥
     * @param content    加密内容
     * @param encodeType {@link #ENCODE_TYPE_HEX}
     * @return
     * @throws Exception
     */
    public static String encryptHMAC(String KEY_MAC, String key, String content, int encodeType) throws Exception {
        Mac mac = Mac.getInstance(KEY_MAC);
        SecretKey secretKey = new SecretKeySpec(key.getBytes(DEFAULT_CHARSET), mac.getAlgorithm());
        mac.init(secretKey);
        byte[] data = mac.doFinal(content.getBytes(DEFAULT_CHARSET));
        if (encodeType == ENCODE_TYPE_BASE64) {
            return Base64.encodeToString(data, Base64.NO_WRAP);
        }
        return bytesToHex(data);
    }

    /**
     * 默认HMAC加密
     *
     * @param key     私钥
     * @param content 加密内容
     * @return
     */
    public static String encryptHMAC(String key, String content, int encodeType) {
        try {
            return encryptHMAC(DEFAULT_HMAC, key, content, encodeType);
        } catch (Exception ex) {
            return content;
        }
    }

    /**
     * 按照红黑树（Red-Black tree）的 NavigableMap 实现
     * 按照字母大小排序
     */
    public static Map<String, Object> sort(Map<String, Object> map) {
        if (map == null) return null;
        Map<String, Object> result = new TreeMap<>(new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                return o1.toString().compareTo(o2.toString());
            }
        });
        result.putAll(map);
        return result;
    }


    /**
     * 组合参数
     *
     * @param map
     * @return 如：key1Value1Key2Value2....
     */
    public static String groupStringParam(Map<String, Object> map) {
        if (map == null) return null;
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> item : map.entrySet()) {

            if (item.getValue() == null || TextUtils.isEmpty(item.getValue().toString())) {
                continue;
            }

            sb.append(item.getKey());
            sb.append(item.getValue());
//
//            if (item.getValue() instanceof Map) {
//                try {
//                    JSONObject obj = new JSONObject();
//                    Map<String, Object> dataMap = (Map<String, Object>) item.getValue();
//                    for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
//                        obj.put(entry.getKey(), entry.getValue());
//                    }
//
////                    sb.append(obj.toString().replace("\\/","/"));
//                    sb.append(obj.toString());
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//            } else {
//                sb.append(item.getValue());
//            }
        }
        return sb.toString();
    }

    /**
     * 转成URL 参数
     *
     * @param map
     * @return
     */
    public static String toStringParams(Map<String, String> map) {
        return toStringParams(map, false);
    }

    /**
     * 转成URL 参数
     *
     * @param map
     * @return 如：key1=value1&key2=value2
     */
    public static String toStringParams(Map<String, String> map, boolean enableUrlEncode) {
        Uri.Builder builder = new Uri.Builder();
        for (Map.Entry<String, String> item : map.entrySet()) {

            if (TextUtils.isEmpty(item.getKey()) || TextUtils.isEmpty(item.getValue()))
                continue;

            String value = item.getValue();
            if (enableUrlEncode) {
                value = URLEncoder.encode(value);
            }
            builder.appendQueryParameter(item.getKey(), value);
        }
        return builder.build().getQuery();
    }

    /**
     * 对map 参数签名后返回，采用HMAC加密方式
     *
     * @return 签好名的sign
     */
    public static String generateSign(String appSecret, String url, Map<String, Object> map) {
        return generateSign(false, ENCODE_TYPE_BASE64, appSecret, url, map);
    }

    /**
     * 对map 参数签名后返回，采用HMAC加密方式
     *
     * @return 签好名的sign
     */
    public static String generateSign(boolean debugable, int encodeType, String appSecret, String url, Map<String, Object> map) {
        if (map == null || TextUtils.isEmpty(appSecret)) return null;

        // 1、对data参数进行重新排序
        Map<String, Object> sortMap = sort(map);

        // 2、拼接参数：key1Value1key2Value2
        String urlParams = groupStringParam(sortMap);
        if (debugable) {
            LogUtils.i("rae", "------> 准备签名：" + url + "; 密钥：" + appSecret);
            LogUtils.i("rae", "------> 1、排序后拼接参数：" + urlParams);
        }

        // 3、拼接准备排序： stringURI + stringParams + AppSecret
        String signString = url + urlParams + appSecret;

        if (debugable) {
            LogUtils.i("rae", "------> 2、签名的字符串：" + signString);
        }

        // 4、私钥签名
        String sign = encryptHMAC(appSecret, signString, encodeType);

        // HEX 转成小写
        if (!TextUtils.isEmpty(sign) && encodeType == ENCODE_TYPE_HEX) {
            sign = sign.toLowerCase();
        }
        if (debugable) {
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, Object> entry : sortMap.entrySet()) {
                sb.append(entry.getKey());
                sb.append(":");
                sb.append(entry.getValue());
                sb.append("\n");
            }
            LogUtils.i("rae", "------> 3、签名参数：\n" + sb.toString());
            LogUtils.i("rae", "------> 4、签名结果：" + sign);
            LogUtils.i("rae", "----------------------------------");
        }

        return sign;
    }
}
